package ru.fadeev.crammer.ui.window;


import java.io.File;

import java.awt.*;
import java.awt.event.*;

import java.sql.SQLException;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreePath;

import ru.fadeev.crammer.*;
import ru.fadeev.crammer.data.*;
import ru.fadeev.crammer.ui.ArrowOptionPane;
import ru.fadeev.crammer.ui.ChooserXlsFileFilter;
import ru.fadeev.crammer.ui.CrammerToolbarButton;
import ru.fadeev.crammer.ui.CrammerToolbarToggleButton;
import ru.fadeev.crammer.ui.treetable.AbstractTreeTableModel;
import ru.fadeev.crammer.ui.treetable.JTreeTable;
import ru.fadeev.crammer.ui.treetable.TreeTableCellRenderer;
import ru.fadeev.crammer.ui.treetable.TreeTableModel;

import org.jdesktop.jdic.tray.*;


/**
 * The CrammerMainFrame class provides GUI for main window of Crammer program
 * @author Konstantin Fadeyev
 *
 */
public class CrammerMainFrame extends JFrame 
		implements ProgressBar, TestNeedsListener{
	// UI Components
	private JMenuBar menuBar;
	private JMenu menuCrammer;
	private JMenu menuCrammerNew;
	private JMenuItem menuCrammerNewDictItem;
	private JMenuItem menuCrammerNewSheetItem;
	private JMenuItem menuCrammerEdit;
	private JMenuItem menuCrammerDelete;
	private JMenuItem menuCrammerImport;
	private JMenuItem menuCrammerExport;
	private JMenuItem menuCrammerSettings;
	private JMenuItem menuCrammerExit;
	
	private JMenu menuTest;
	private JRadioButtonMenuItem menuTestTranslation;
	private JRadioButtonMenuItem menuTestReverse;
	private JRadioButtonMenuItem menuTestTranscription;
	private JRadioButtonMenuItem menuTestGapsFilling;
	private JMenuItem menuTestStartTest;
	
	private JMenu menuHelp;
	
	private JPanel mainPanel;
	
	private JToolBar toolBar;
	private JToggleButton newButton;
	private JPopupMenu popupNew;
	private JMenuItem popupNewDictItem;
	private JMenuItem popupNewSheetItem;
	private JButton editButton;
	private JButton deleteButton;
    private JToggleButton testTranslationButton;
    private JToggleButton testReverseButton;
    private JToggleButton testTranscriptionButton;
    private JToggleButton testGapsFillingButton;
    private JButton startTestButton;
    private JButton settingsButton;
    
	private JPanel dictionaryTablePanel;
	private JPanel dictionaryLabelPanel;
	private JPanel progressPanel;
	private JPanel progressBarLabelPanel;
	private JLabel progressBarLabel;
	private JPanel progressBarPanel;
	private JProgressBar progressBar;
	
	private DictionaryTreeTableModel dictionaryTreeTableModel;
	private DictionaryTreeTable dictionaryTreeTable;
	private JScrollPane dictionaryScrollPane;

    private JPopupMenu dictionaryPopupMenu;
	private JMenu dictionaryPopupNew;
	private JMenuItem dictionaryPopupNewDictItem;
	private JMenuItem dictionaryPopupNewSheetItem;
	private JMenuItem dictionaryPopupEditItem;
	private JMenuItem dictionaryPopupDeleteItem;

	private JLabel dictionaryWordsCountLabel;

	private JPopupMenu trayMenu;
	private JMenuItem trayMenuExpandItem;
	private JMenuItem trayMenuQuitItem;
	private TrayIcon trayIcon;
	
	// User fields
	private DictionariesManager dictionariesManager;
    private GFIndexer gfIndexer;
    private PHBGetter phbGetter;
    private int repeatTestType;
    private GFIndicesStorage storage;
	private TestManager testManager;
    private TestNeedsWatcher testNeedsWatcher;
    private int toolBarIconsSize;
    private String version; 

  
    
    /**
     * Constructs CrammerMainFrame, initializing program data and GUI components
     * @throws SQLException if cannot initialize database
     */
    public CrammerMainFrame() throws SQLException{
		super();
				
		//Creating and initializing data structures for Crammer
		DatabaseManager.checkDatabaseConsistency();
		Settings.load();	
		//Settings.refreshProxySystemProperties();
		
        dictionariesManager = new DictionariesManager();  
        
		storage = new GFIndicesStorage();
		gfIndexer = new GFIndexer(storage, this);
		gfIndexer.setPriority(Thread.MIN_PRIORITY);	
		phbGetter = new PHBGetter(storage);
		repeatTestType = -1; 
        testManager = new TestManager();
        testNeedsWatcher = new TestNeedsWatcher(dictionariesManager);
        testNeedsWatcher.setPriority(Thread.MIN_PRIORITY);
        testNeedsWatcher.setTestNeedsListener(this);
        toolBarIconsSize = 24;       
        version = "2.0.2";
        
		//Creating and initializing UI components for Crammer
		ImageIcon crammerIcon = new ImageIcon("icons/16/crammer.png");
		setIconImage(crammerIcon.getImage());
		setTitle("Crammer");
		
		
		initComponents();
		placeComponents();
					                    
        refreshDictionaryWordsCountLabel();
		refreshDictionaryTreeTableButtons();  
        refreshStartTestButton();
                      
        chooseTestAction(Settings.getInt("lastTestType")); 
           
        
        //Starting background processes
        gfIndexer.start();
        phbGetter.start();
        testNeedsWatcher.start();
        
        //Adding listeners
        dictionariesManager.setWordsDataListener(new WordsDataListener() {
        	public void elementInserted(int index) {
        		dictionaryTreeTableModel.dictionaryInserted(index);
        	}
        	
        	public void testWordsCountChanged(final int change, int testType, 
        			boolean unchecked, final int dictionaryIndex, 
        			final int sheetIndex) {
        		if(testType != testManager.getCurrentTestType()) {
        			return;
        		}
           		if(!SwingUtilities.isEventDispatchThread()) {
	    			SwingUtilities.invokeLater(new Runnable() {
	    				public void run() {
	    					refreshColors(change, dictionaryIndex, sheetIndex);	
	    				}
	    			});  
           		} else {
           			refreshColors(change, dictionaryIndex, sheetIndex);
           		}
        	}
        	
        	public void wordsCountChanged(int change) {
        		if(!SwingUtilities.isEventDispatchThread()) {
       			 	SwingUtilities.invokeLater(new Runnable() {
       			 		public void run() {
       			 			refreshDictionaryWordsCountLabel();
       			 		}
       			 	});
       			} else {
       				refreshDictionaryWordsCountLabel();
       			}      		
        	}
        });
        
        addWindowListener(
        		new WindowAdapter() {
					public void windowClosing(WindowEvent e) {
						if(Settings.getBoolean("minimizeOnClose")) {
							setExtendedState(ICONIFIED);
						} else {
							exitAction();
						}
					}
					
					public void windowIconified(WindowEvent e) {
						if(Settings.getBoolean("minimizeToTray")) {
							setVisible(false);
						}
					}
				});
	}
  
    
    /**
	 * Launches about dialog
	 */
    public void aboutAction() {
        JOptionPane.showMessageDialog(this, "Crammer v." + version +
        		"\nCreated by Konstantin Fadeyev", 
        		"About", JOptionPane.INFORMATION_MESSAGE);
    }
  
    
    /**
     * Launches adding dictionary procedure
     */
    private void addDictionary() {
        Dictionary newDictionary = new Dictionary();
    	SpecifyDictionaryNameDialog dialog = new SpecifyDictionaryNameDialog(this, 
    			newDictionary, SpecifyDictionaryNameDialog.ADD_MODE );
    	dialog.setVisible(true);
    	if(dialog.getPressedButton() == SpecifyDictionaryNameDialog.BTN_OK) {
    		dictionariesManager.addDictionary(newDictionary, storage);
    		dictionaryTreeTableModel.dictionaryInserted(
    				dictionariesManager.size() - 1);
    		dictionaryTreeTable.setSelectedDictionary(
    				dictionariesManager.size() - 1);
    	}
    }
 
    
    /**
     * Launches adding sheet procedure
     */
    private void addSheet() {
    	int dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
    	Dictionary dictionary = dictionariesManager.getDictionary(
    			dictionaryIndex);
    	
    	AddEditSheetDialog dialog = 
    		new AddEditSheetDialog(this, dictionary, storage, dictionaryIndex);
    	dialog.setVisible(true);   	
    	if(dialog.getPressedButton() == AddEditSheetDialog.BTN_OK) {
    		dictionaryTreeTableModel.dictionaryChanged(dictionaryIndex);
    		dictionaryTreeTableModel.sheetInserted(dictionaryIndex, 
    				dictionary.size() - 1);
    		dictionaryTreeTable.setSelectedSheet(dictionaryIndex, 
    				dictionary.size() - 1);
    		refreshSheetTestNeeds(dictionaryIndex, dictionary.size()-1);
    	}   	
    }
 
    
    /**
     * Sets current test type defined by 'testType'
     * @param testType is one of the test types defined in class TestManager
     */
    private void chooseTestAction(int testType) {    			
		testTranslationButton.setSelected(false);
		menuTestTranslation.setSelected(false);
		testReverseButton.setSelected(false);
		menuTestReverse.setSelected(false);
		testTranscriptionButton.setSelected(false);
		menuTestTranscription.setSelected(false);
		testGapsFillingButton.setSelected(false);
		menuTestGapsFilling.setSelected(false);	
		
		switch(testType) {
		case TestManager.TRANSLATION:
			testTranslationButton.setSelected(true);
			menuTestTranslation.setSelected(true);
			break;
		case TestManager.REVERSE_TRANSLATION:
			testReverseButton.setSelected(true);
			menuTestReverse.setSelected(true);
			break;
		case TestManager.TRANSCRIPTION:
			testTranscriptionButton.setSelected(true);
			menuTestTranscription.setSelected(true);
			break;
		case TestManager.GAPS_FILLING:
			testGapsFillingButton.setSelected(true);
			menuTestGapsFilling.setSelected(true);
			break;
		}
		
    	if(testManager.getCurrentTestType() != testType) {   
    		testManager.setCurrentTestType(testType); 
    		dictionaryTreeTable.fireTableDataChanged(true);        	
        	refreshStartTestButton();
        	refreshDictionaryWordsCountLabel();
    	} 	
    }
 
    
    /**
     * Creates and initialize UI components
     */
    private void initComponents() {        
		// Menu
		menuBar = new JMenuBar();
        initMenuCrammer();
        initMenuTest();
		initMenuHelp();
		menuBar.add(menuCrammer);
		menuBar.add(menuTest);
        menuBar.add(menuHelp);
		setJMenuBar(menuBar);

		// Main Panel and Tool Bar
		mainPanel = new JPanel();
	    initToolBar();

		// Popup Menus
		initDictionaryPopupMenu();

		//Dictionary table and label
        initDictionaryTreeTable();        
		dictionaryTablePanel = new JPanel();
		dictionaryWordsCountLabel = new JLabel();
		dictionaryLabelPanel = new JPanel();

		//Progress bar
		progressPanel = new JPanel();
		progressBarLabelPanel = new JPanel();
    	progressBarLabel = new JLabel();
    	progressBarLabel.setHorizontalAlignment(JLabel.RIGHT);
		progressBarPanel = new JPanel();
		progressBar = new JProgressBar();
		hidePB();
			
		setContentPane(mainPanel);
        initSystemTray();
    }

    
    /**
     * Delete action event handler
     */
    private void deleteAction() {
    	if(dictionaryTreeTable.getSelectedComponent() instanceof Dictionary) {
    		int dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
    		deleteDictionary(dictionaryIndex);
    	} else if(dictionaryTreeTable.getSelectedComponent() instanceof Sheet) {
    		int dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
    		int sheetIndex = dictionaryTreeTable.getSelectedSheet();
    		deleteSheet(dictionaryIndex, sheetIndex);
    	}
    }

    
    /**
     * Launches deleting dictionary procedure
     * @param dictionaryIndex index of dictionary to delete
     */
    public void deleteDictionary(int dictionaryIndex) {
		int n = ArrowOptionPane.showConfirmDialog(this,
				"Selected dictionary will be deleted.\n" +
                "Are you sure?","Are you sure?", JOptionPane.YES_NO_OPTION);
	    switch(n) {
        case JOptionPane.OK_OPTION:        	
        	int indexToSelect = dictionaryIndex;
        	
        	dictionaryTreeTableModel.dictionaryRemoved(dictionaryIndex);
 	
        	dictionariesManager.removeDictionary(dictionaryIndex, storage);
			if(indexToSelect == dictionariesManager.size()) {
				indexToSelect = indexToSelect - 1;
			}
			if(indexToSelect >= 0) {
				dictionaryTreeTable.setSelectedDictionary(indexToSelect);
			}		
            break;
	    }    	
    }

    
    /**
     * Launches deleting sheet procedure
     * @param dictionaryIndex index of dictionary we delete sheet from
     * @param sheetIndex index of sheet to delete
     */
	private void deleteSheet(int dictionaryIndex, int sheetIndex) {
		int n = ArrowOptionPane.showConfirmDialog(this,
				"Selected sheets will be deleted.\n" +
                "Are you sure?","Are you sure?", JOptionPane.YES_NO_OPTION);
	    switch(n) {
        case JOptionPane.OK_OPTION:        // delete sheets
        	Dictionary dictionary = dictionariesManager.getDictionary(
        			dictionaryIndex);
        	int indexToSelect = sheetIndex;
        	       	
        	dictionaryTreeTableModel.sheetRemoved(dictionaryIndex, sheetIndex);
    	
        	dictionary.removeSheet(sheetIndex, storage);
        	
			if(indexToSelect == dictionary.size()) {
				indexToSelect = indexToSelect - 1;
			}
			if(indexToSelect >= 0) {
				dictionaryTreeTable.setSelectedSheet(dictionaryIndex, 
						indexToSelect);
			} else {
				dictionaryTreeTable.setSelectedDictionary(dictionaryIndex);
			}
            break;
        default:       // nothing to do
            return;
	    }
	}

  
    /**
     * Edit action event handler
     */
    private void editAction() {
    	int dictionaryIndex;
    	int sheetIndex;
    	
    	if(dictionaryTreeTable.getSelectedComponent() instanceof Dictionary) {
    		dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
    		editDictionary(dictionaryIndex);
    	} else if(dictionaryTreeTable.getSelectedComponent() instanceof Sheet) {
    		dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
    		sheetIndex = dictionaryTreeTable.getSelectedSheet();
    		editSheet(dictionaryIndex, sheetIndex);
    	}
    }
      
    /**
     * Launches editing dictionary procedure
     * @param index index of dictionary for editing
     */
    public void editDictionary(int index) {
    	Dictionary currentDictionary = dictionariesManager.getDictionary(index);
    	Dictionary currentDictionaryCopy = new Dictionary();
    	currentDictionaryCopy.setName(currentDictionary.getName());
    	
    	SpecifyDictionaryNameDialog dialog = new SpecifyDictionaryNameDialog(this, 
    			currentDictionaryCopy, SpecifyDictionaryNameDialog.EDIT_MODE );
    	dialog.setVisible(true);
    	if(dialog.getPressedButton() == SpecifyDictionaryNameDialog.BTN_OK) {
    		currentDictionary.setName(currentDictionaryCopy.getName());
			dictionariesManager.updateDictionary(index);
			dictionaryTreeTableModel.dictionaryChanged(index);
			dictionaryTreeTable.setSelectedDictionary(index);
    	}
    }

    
    /**
     * Launches editing sheet procedure
     * @param dictionaryIndex index of dictionary we edit sheet in
     * @param sheetIndex index of sheet for editing
     */
	private void editSheet(int dictionaryIndex, int sheetIndex) {
		Dictionary dictionary = dictionariesManager.getDictionary(dictionaryIndex);
    	
    	AddEditSheetDialog dialog = 
    		new AddEditSheetDialog(this, dictionary, 
    				sheetIndex, storage, dictionaryIndex);
    	dialog.setVisible(true);
    	
    	if(dialog.getPressedButton() == AddEditSheetDialog.BTN_OK) {
    		dictionaryTreeTableModel.sheetChanged(dictionaryIndex, sheetIndex);
    		dictionaryTreeTable.setSelectedSheet(dictionaryIndex, sheetIndex);
			refreshSheetTestNeeds(dictionaryIndex,sheetIndex);
    	}
	}
    
    
    /**
     * Exit action handler. Finishes all processes and save data  
     */
    public void exitAction() {
    	// Saving window size and location   	
    	Settings.setInt("crammerMainFrameX", getX());
    	Settings.setInt("crammerMainFrameY", getY());
    	Settings.setInt("crammerMainFrameW", getWidth());
    	Settings.setInt("crammerMainFrameH", getHeight());
  		Settings.setBoolean("crammerMainFrameMaximized", 
  				(getExtendedState() == MAXIMIZED_BOTH));
  		Settings.setInt("lastTestType", testManager.getCurrentTestType());
  		
    	dictionariesManager.saveToTestValues();
    	Thread closingThread = new Thread() {
    		public void run() {
    			testNeedsWatcher.stopWatcher();
    			gfIndexer.stopIndexer();
    			phbGetter.stopGetter();
    	        DatabaseManager.shutdownDB();
    	        System.exit(0);
    		}
    	};   	
    	
    	setVisible(false);
    	SystemTray tray = SystemTray.getDefaultSystemTray();
    	tray.removeTrayIcon(trayIcon);
    	
    	closingThread.start();
    }

    
    /**
     * Launches export procedure
     */
    private void exportAction() {
    	JFileChooser chooser = new JFileChooser();
    	File startDirectory = new File(Settings.getString("lastImportedExported"));
    	if(startDirectory.exists()) {
    		chooser.setCurrentDirectory(startDirectory);
    	} else {
    		chooser.setCurrentDirectory(new File("."));
    	}
    	chooser.setFileFilter(new ChooserXlsFileFilter());
    	
    	int returnVal = chooser.showSaveDialog(this);
    	if (returnVal == JFileChooser.APPROVE_OPTION) {
    		File tempFile = chooser.getSelectedFile();
    		
    		ChooserXlsFileFilter textFilter = new ChooserXlsFileFilter();
        	if(chooser.getFileFilter().getDescription().equals(
        			textFilter.getDescription()) ) {
        		if(! tempFile.getName().endsWith(".xls")) {
        			String fileName =  tempFile.getName() + ".xls";
        			tempFile = new File(tempFile.getParent(), fileName);
        		}
        	}
        	File file = tempFile;     	
    		Settings.setString("lastImportedExported", file.getParent());
    		if(dictionariesManager.exportToFile(file)) {
    			JOptionPane.showMessageDialog(this,
       					"Export completed successfully.",
       	                "Message", JOptionPane.INFORMATION_MESSAGE);
    		} else {
    			JOptionPane.showMessageDialog(this, 
    					"Error occured during export.",
                		"Error", JOptionPane.ERROR_MESSAGE);
    		}	
    	}
    }

    
    /**
	 * Implements ProgressBar interface getPBValue function
	 */
    public int getPBValue() {
    	return progressBar.getValue();
    }
    
    
    /**
	 * Implements ProgressBar interface hidePB function
	 */
	public void hidePB() {
		progressBar.setVisible(false);
		progressBarLabel.setVisible(false);	
	}

    
    /**
     * Launches import procedure
     */
    private void importAction(){
    	JFileChooser chooser = new JFileChooser();
    	File startDirectory = new File(Settings.getString("lastImportedExported"));
    	if(startDirectory.exists()) {
    		chooser.setCurrentDirectory(startDirectory);
    	} else {
    		chooser.setCurrentDirectory(new File("."));
    	}
    	chooser.setFileFilter(new ChooserXlsFileFilter());
    	
    	int returnVal = chooser.showOpenDialog(this);
    	   	
    	if (returnVal == JFileChooser.APPROVE_OPTION) {
        	File file = chooser.getSelectedFile();	
    		Settings.setString("lastImportedExported", file.getParent());
        	if(dictionariesManager.importFromFile(file, storage)) {
        		testNeedsWatcher.recheck();
    			JOptionPane.showMessageDialog(this,
       					"Import completed successfully.",
       	                "Message", JOptionPane.INFORMATION_MESSAGE);
        	} else {
        		JOptionPane.showMessageDialog(this, 
        				"Error occured during import. \n" +
                		"Make sure that file has right format. ",
                		"Error", JOptionPane.ERROR_MESSAGE);
        	}
    	}    	
    }
    
	
	/**
	 * Initializes and shows Crammer window
	 */
    public void init() {
    	int x = Settings.getInt("crammerMainFrameX");
    	int y = Settings.getInt("crammerMainFrameY");
    	int w = Settings.getInt("crammerMainFrameW");
    	int h = Settings.getInt("crammerMainFrameH");
    	boolean maximized = Settings.getBoolean("crammerMainFrameMaximized");
    	
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        if(x<0 || y<0 || w<0 || h<0) {
        	x = screenSize.width/20;
        	y = screenSize.height/20;
        	w = screenSize.width * 9/10;
        	h = screenSize.height* 8/10;
        }
        setSize(w, h);
    	setLocation(x, y);    
		
    	if(maximized) {
    		setExtendedState(MAXIMIZED_BOTH);
    	}
    	
		setVisible(true);	        
	}
    
    
    /**
     * Initializes Popup menu for dictionary table 
     */
    private void initDictionaryPopupMenu() {
        dictionaryPopupMenu = new JPopupMenu();

        dictionaryPopupNew = new JMenu("New");
    	ImageIcon newDictIcon = new ImageIcon("icons/16/newDictionary.png");
    	dictionaryPopupNewDictItem = new JMenuItem("Dictionary", newDictIcon);
    	
    	ImageIcon newSheetIcon = new ImageIcon("icons/16/newSheet.png");
    	dictionaryPopupNewSheetItem = new JMenuItem("Sheet", newSheetIcon);
		
        dictionaryPopupNew.add(dictionaryPopupNewDictItem);
        dictionaryPopupNew.add(dictionaryPopupNewSheetItem);	

        dictionaryPopupEditItem = new JMenuItem("Edit...");
		dictionaryPopupDeleteItem = new JMenuItem("Delete");
		
		dictionaryPopupMenu.add(dictionaryPopupNew);
		dictionaryPopupMenu.addSeparator();
        dictionaryPopupMenu.add(dictionaryPopupEditItem);
        dictionaryPopupMenu.add(dictionaryPopupDeleteItem);
		
		dictionaryPopupNewDictItem.addActionListener(
				new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						dictionaryPopupMenu.setVisible(false);
						addDictionary();
					}
				});

		dictionaryPopupNewSheetItem.addActionListener(
				new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						dictionaryPopupMenu.setVisible(false);
						addSheet();
					}
				});

		dictionaryPopupEditItem.addActionListener(
				new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                dictionaryPopupMenu.setVisible(false);
		                editAction();
		            }
		        });
		
        dictionaryPopupDeleteItem.addActionListener(
        		new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                dictionaryPopupMenu.setVisible(false);
		                deleteAction();
		            }
        		});
    }   
    
    
    /**
     * Initializes Dictionaries TreeTable
     */
	private void initDictionaryTreeTable() {	
		dictionaryTreeTableModel = 
			new DictionaryTreeTableModel(testManager, dictionariesManager);
		
		dictionaryTreeTable = new DictionaryTreeTable(
				dictionaryTreeTableModel, testManager);
		
		dictionaryTreeTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
		dictionaryTreeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

		
		dictionaryTreeTable.getColumnModel().getColumn(0).setPreferredWidth(800);
		dictionaryTreeTable.getColumnModel().getColumn(1).setPreferredWidth(200);
		dictionaryTreeTable.getColumnModel().getColumn(2).setPreferredWidth(100);
		dictionaryTreeTable.getTableHeader().setReorderingAllowed(false);
				
		ListSelectionListener listener = 
			new ListSelectionListener() {
				public void valueChanged(ListSelectionEvent e) {
					refreshDictionaryTreeTableButtons();	
				}
			};
		dictionaryTreeTable.getSelectionModel()
				.addListSelectionListener(listener);
		dictionaryTreeTable.getColumnModel().getSelectionModel()
	        	.addListSelectionListener(listener);
			
		dictionaryScrollPane = new JScrollPane(dictionaryTreeTable);
		dictionaryScrollPane.getViewport().setBackground(Color.WHITE);
		dictionaryTreeTable.setSelectionForeground(Color.BLACK);
		
		dictionaryTreeTable.addMouseListener(
				new MouseAdapter() {
					public void mouseClicked(MouseEvent ev) {
						dictionaryPopupMenu.setVisible(false);
						if(ev.getButton() == MouseEvent.BUTTON3) {
							int row = dictionaryTreeTable.rowAtPoint(
									ev.getPoint());
							if(!dictionaryTreeTable.isRowSelected(row))	{
								dictionaryTreeTable.clearSelection();
								dictionaryTreeTable.setRowSelectionInterval(
										row, row);
							}
							dictionaryPopupMenu.show(dictionaryTreeTable, 
									ev.getPoint().x, ev.getPoint().y);
						}
						
						if((ev.getButton() == MouseEvent.BUTTON1) 
								&& (ev.getClickCount() == 2)) {
							int column = dictionaryTreeTable.columnAtPoint(
									ev.getPoint());
							if(column != 2) {
								if(dictionaryTreeTable.getSelectedComponent() 
										instanceof Sheet) {
									editAction();
								}
							}
						}
					}
				});

		dictionaryScrollPane.addMouseListener(
				new MouseAdapter() {
					public void mouseClicked(MouseEvent ev) {
						dictionaryTreeTable.clearSelection();
						
						dictionaryPopupMenu.setVisible(false);
						if(ev.getButton() == MouseEvent.BUTTON3) {
							dictionaryPopupMenu.show(dictionaryScrollPane, 
									ev.getPoint().x, ev.getPoint().y);
						}
					}
				});
	}

	
	/**
	 * Implements ProgressBar Interface isPBVisible function
	 */
	public boolean isPBVisible() {
		return progressBar.isVisible();
	}

    
    /**
     * Initializes Crammer menu 
     * ('Crammer' - is a name of menu instead of usual 'File' menu)
     */
    private void initMenuCrammer() {
        menuCrammer = new JMenu("Crammer");

        menuCrammerNew = new JMenu("New");
    	ImageIcon newDictIcon = new ImageIcon("icons/16/newDictionary.png");
        menuCrammerNewDictItem = new JMenuItem("Dictionary", newDictIcon);

        ImageIcon newSheetIcon = new ImageIcon("icons/16/newSheet.png");
        menuCrammerNewSheetItem = new JMenuItem("Sheet", newSheetIcon);
		
        menuCrammerNew.add(menuCrammerNewDictItem);
        menuCrammerNew.add(menuCrammerNewSheetItem);		
    	
    	menuCrammerEdit = new JMenuItem("Edit...");
    	menuCrammerDelete = new JMenuItem("Delete");
    	
		ImageIcon importIcon = new ImageIcon("icons/16/import.png");
    	menuCrammerImport = new JMenuItem("Import...", importIcon);
    	
		ImageIcon exportIcon = new ImageIcon("icons/16/export.png");
		menuCrammerExport = new JMenuItem("Export...", exportIcon);

    	
		ImageIcon settingsIcon = new ImageIcon("icons/16/settings.png");
		menuCrammerSettings = new JMenuItem("Settings", settingsIcon);
        
        menuCrammerExit = new JMenuItem("Exit");

		menuCrammer.add(menuCrammerNew);
		menuCrammer.addSeparator();
		menuCrammer.add(menuCrammerEdit);
		menuCrammer.add(menuCrammerDelete);
		menuCrammer.addSeparator();
		menuCrammer.add(menuCrammerImport);
		menuCrammer.add(menuCrammerExport);
		menuCrammer.addSeparator();
        menuCrammer.add(menuCrammerSettings);
		menuCrammer.addSeparator();
		menuCrammer.add(menuCrammerExit);	
        
		menuCrammerNewDictItem.addActionListener(
				new AbstractAction() {
					public void actionPerformed(ActionEvent e) {				
						addDictionary();
					}
				});

		menuCrammerNewSheetItem.addActionListener(
				new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						addSheet();
					}
				});
    	
		menuCrammerEdit.addActionListener(
				new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		            	editAction();
		            }
		        });       
               
		menuCrammerDelete.addActionListener(
				new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		            	deleteAction();
		            }
		        });  
		
		menuCrammerImport.addActionListener(
				new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                importAction();
		            }
		        });
        
        menuCrammerExport.addActionListener(
        		new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                exportAction();
		            }
		        });
		
        menuCrammerSettings.addActionListener(
        		new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                settingsAction();
		            }
		        });

        menuCrammerExit.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						exitAction();
					}
				});
    }

    
    /**
     * Initializes Help menu
     */
    private void initMenuHelp() {
        menuHelp = new JMenu("Help");
        JMenuItem menuHelpAbout = new JMenuItem("About...");
        menuHelp.add(menuHelpAbout);
               
        menuHelpAbout.addActionListener(
        		new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		                aboutAction();
		            }
		        });
    }
    
    
    /**
     * Initializes Test menu
     */
    private void initMenuTest() {
        menuTest = new JMenu("Test");
		
		ImageIcon testTranslationIcon = 
			new ImageIcon("icons/16/testTranslation.png");
		menuTestTranslation = new JRadioButtonMenuItem(
				"Translation", testTranslationIcon);
		
		ImageIcon testReverseTranslationIcon = 
			new ImageIcon("icons/16/testReverseTranslation.png");	
		menuTestReverse = new JRadioButtonMenuItem(
				"Reverse Translation", 
					testReverseTranslationIcon);

		ImageIcon testTranscriptionIcon = 
			new ImageIcon("icons/16/testTranscription.png");	
		menuTestTranscription = new JRadioButtonMenuItem(
				"Transcription", testTranscriptionIcon);
		
		ImageIcon menuTestGapsFillingIcon = 
			new ImageIcon("icons/16/testGapsFilling.png");	
		menuTestGapsFilling = new JRadioButtonMenuItem(
				"Gaps Filling", 
				menuTestGapsFillingIcon);
		
		ImageIcon startTestIcon = new ImageIcon("icons/16/startTest.png");
		menuTestStartTest = new JMenuItem("Start Test", startTestIcon);

        menuTest.add(menuTestTranslation);
        menuTest.add(menuTestReverse);
        menuTest.add(menuTestTranscription);
        menuTest.add(menuTestGapsFilling);
        menuTest.addSeparator();
        menuTest.add(menuTestStartTest);
		
        menuTestTranslation.addActionListener(
        		new AbstractAction() {
		            public void actionPerformed(ActionEvent e) {
		            	chooseTestAction(TestManager.TRANSLATION);
		            }
		        });

        menuTestReverse.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						chooseTestAction(TestManager.REVERSE_TRANSLATION);
					}
				});

        menuTestTranscription.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						chooseTestAction(TestManager.TRANSCRIPTION);
					}
				});

        menuTestGapsFilling.addActionListener(
        		new AbstractAction() {
        			public void actionPerformed(ActionEvent e) {
        				chooseTestAction(TestManager.GAPS_FILLING);
        			}
        		});

        menuTestStartTest.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						testAction();
					}
        		});
    }
    
    
    /**
     * Implements TestNeedsListener interface needToTest function
     */
    public void needToTest(int testType) {
    	repeatTestType = testType;
    	if(getExtendedState() == ICONIFIED) {
    		trayIcon.displayMessage("Time to repeat",
    				"There is 10 words to repeat",
    				TrayIcon.INFO_MESSAGE_TYPE);
    	}
    }
    
    /**
     * Places UI components using different layout managers
     */
    private void placeComponents() {
    	mainPanel.setLayout(new BorderLayout());
    	mainPanel.add(toolBar,BorderLayout.NORTH);
    	mainPanel.add(dictionaryTablePanel, BorderLayout.CENTER);
    	
    	dictionaryTablePanel.setLayout(new BorderLayout());
		dictionaryTablePanel.add(dictionaryScrollPane, BorderLayout.CENTER);
		dictionaryTablePanel.add(dictionaryLabelPanel, BorderLayout.SOUTH);
		
		dictionaryLabelPanel.setPreferredSize(new Dimension(30, 20));
		dictionaryLabelPanel.setLayout(new BorderLayout());
		dictionaryLabelPanel.add(dictionaryWordsCountLabel, BorderLayout.EAST);
		dictionaryLabelPanel.add(progressPanel, BorderLayout.WEST);
		
		progressPanel.setPreferredSize(new Dimension(320, 20)); 
		progressPanel.setLayout(new BorderLayout());
		progressPanel.add(progressBarLabelPanel, BorderLayout.CENTER);
		progressPanel.add(progressBarPanel, BorderLayout.EAST);
		
		progressBarLabelPanel.setLayout(new BorderLayout());
    	progressBarLabelPanel.add(progressBarLabel, BorderLayout.EAST);
		
    	progressBarPanel.setPreferredSize(new Dimension(125, 20)); 
    	progressBarPanel.setLayout(null);
    	progressBarPanel.add(progressBar, BorderLayout.CENTER);
    	progressBar.setBounds(5, 3, 100, 15);    			
    }
    
    
    /**
     * Event handler for testWordsCountChanged event of DictionariesManager's 
     * TestWordsCountListener . Used to refresh colors in dictionary table 
     * when some word changes its 'need to be tested' status 
     * 
     * @param change of 'need to be tested' words count  
     * @param dictionaryIndex index of dictionary test words count changed in
     * @param sheetIndex index of sheet test words count changed in
     */
    private void refreshColors(int change, int dictionaryIndex,	int sheetIndex) {
		Dictionary dictionary = 
			dictionariesManager.getDictionary(dictionaryIndex);
		int oldIndex = dictionaryTreeTable.getWrongIndex(
			dictionary, change);
		int newIndex = dictionaryTreeTable.getWrongIndex(
			dictionary, 0);
		if(oldIndex != newIndex) {
			dictionaryTreeTableModel.dictionaryChanged(
				dictionaryIndex);
		}		
		if(dictionaryTreeTable.isExpandedDictionary(
				dictionaryIndex)) {
			Sheet sheet = dictionary.getSheet(sheetIndex);
			oldIndex = dictionaryTreeTable.getWrongIndex(
				sheet, change);
			newIndex = dictionaryTreeTable.getWrongIndex(
					sheet, 0);
			if(oldIndex != newIndex) {
				dictionaryTreeTableModel.sheetChanged(
						dictionaryIndex, sheetIndex);
			}
			if(sheet.isChecked(testManager.getCurrentTestType())) {
				dictionaryTreeTableModel.sheetChanged(
						dictionaryIndex, sheetIndex);
			}
		}
		if(dictionary.isChecked(testManager.getCurrentTestType())) {
			dictionaryTreeTableModel.dictionaryChanged(
					dictionaryIndex);
		}
		refreshDictionaryWordsCountLabel();
		refreshStartTestButton();
    }
	
	
	/**
     * Refreshes test needs of elements of dictionary chosen for test that
     * weren't checked yet. Used when user chooses dictionary for test. 
     * @param dictionaryIndex index of dictionary to refresh test needs
     */
    private void refreshDictionaryTestNeeds(int dictionaryIndex) {
    	Dictionary dictionary = dictionariesManager.getDictionary(dictionaryIndex);   	
    	int testType = testManager.getCurrentTestType();
    	if(!dictionary.isChecked(testType)) {
    		for(int i = 0; i < dictionary.size(); i++) {
    			Sheet sheet = dictionary.getSheet(i);
    			if(!sheet.isChecked(testType)) {
    				for(int j = 0; j < sheet.size(); j++) {
    					WordRecord wordRecord = sheet.getWordRecord(j);
    					if(!wordRecord.isChecked(testType)) {
    						wordRecord.refreshNeededToTest(testType, 
    								dictionaryIndex, i);
    					}
    				}
    			}
    		}
    	}
    }

    
    /**
     * Refreshes toolbar and menus buttons those to work with dictionaries and
     * sheets 
     */
    private void refreshDictionaryTreeTableButtons() {
    	Object selectedComponent = dictionaryTreeTable.getSelectedComponent();
  	
    	if(selectedComponent != null)	{
    		if(selectedComponent instanceof Dictionary || 
    				selectedComponent instanceof Sheet) {
    			menuCrammerNewSheetItem.setEnabled(true);
    			menuCrammerEdit.setEnabled(true);
    			menuCrammerDelete.setEnabled(true);
    			popupNewSheetItem.setEnabled(true);
    			editButton.setEnabled(true);
    			deleteButton.setEnabled(true);
    			dictionaryPopupNewSheetItem.setEnabled(true);
    			dictionaryPopupEditItem.setEnabled(true);
    			dictionaryPopupDeleteItem.setEnabled(true);
    			return;
    		}
    	}
    	menuCrammerNewSheetItem.setEnabled(false);
		menuCrammerEdit.setEnabled(false);
		menuCrammerDelete.setEnabled(false);
		popupNewSheetItem.setEnabled(false);
		editButton.setEnabled(false);
		deleteButton.setEnabled(false);
    	dictionaryPopupNewSheetItem.setEnabled(false);
		dictionaryPopupEditItem.setEnabled(false);
		dictionaryPopupDeleteItem.setEnabled(false);
    }
    
    /**
     * Refreshes Words Count Label
     */
    private void refreshDictionaryWordsCountLabel() {
    	dictionaryWordsCountLabel.setText(
    			"<html><b>Total Words (To Test/Total): " 
    			+ dictionariesManager.
    			getToTestWordsCount(testManager.getCurrentTestType()) + "/" +
    			dictionariesManager.getWordsCount() + "  </b></html>");
    }
    
    
    /**
     * Refreshes test needs of elements of sheet chosen for test that
     * weren't checked yet.
	 * Used when user chooses sheet for test. 
     * @param dictionaryIndex index of dictionary to refresh test needs
     * @param sheetIndex index of sheet to refresh test needs
     */
    private void refreshSheetTestNeeds(int dictionaryIndex, int sheetIndex) {
    	Dictionary dictionary = dictionariesManager.getDictionary(dictionaryIndex);
    	Sheet sheet = dictionary.getSheet(sheetIndex);
		int testType = testManager.getCurrentTestType();
		if(!sheet.isChecked(testType)) {
			for(int j = 0; j < sheet.size(); j++) {
				WordRecord wordRecord = sheet.getWordRecord(j);
				if(!wordRecord.isChecked(testType)) {
					wordRecord.refreshNeededToTest(testType, dictionaryIndex, sheetIndex);
				}
			}
		}
    }
    
    
    /**
     * Refreshes enablability of Start Test Button due chosen to test words count
     */
    private void refreshStartTestButton() {
    	if(dictionariesManager.getToTestWordsCount(
    			testManager.getCurrentTestType()) > 0) {
    		startTestButton.setEnabled(true);
    		menuTestStartTest.setEnabled(true);
    		startTestButton.setToolTipText("Start testing");
    	} else {
    		startTestButton.setEnabled(false);
    		menuTestStartTest.setEnabled(false);
    		startTestButton.setToolTipText("Start testing " +
    				"(choose some words for test first)");
    	}
    }

    
    /**
	 * Refreshes test needs of all dictionaries chosen for test. 
	 * Used before starting any test to check that for every word 
	 * that chosen to test we definitely know whether to test it or not.
	 * @param testType test type for which we refreshes test needs
	 */
    private void refreshTestNeeds(int testType) {
		for(int i = 0; i < dictionariesManager.size(); i++) {
			Dictionary dictionary = dictionariesManager.getDictionary(i);
			if(dictionary.isToTest()) {
				if(!dictionary.isChecked(testType)) {
					for(int j = 0; j < dictionary.size(); j++) {
						Sheet sheet = dictionary.getSheet(j);
						if(sheet.isToTest()) {
							if(!sheet.isChecked(testType)) {
		    					for(int k = 0; k < sheet.size(); k++) {
		    						WordRecord wordRecord = 
		    							sheet.getWordRecord(k);
		    						wordRecord.refreshNeededToTest(
		    								testType, i, k);
		    					}
		    				}
						}			
					}
	    		}
			}			
		}
    }
    
    
    /**
	 * Implements ProgressBar Interface setPBNote function
	 */
	public void setPBNote(final String note) {
   		if(!SwingUtilities.isEventDispatchThread()) {
   			SwingUtilities.invokeLater(new Runnable() {
   				public void run() {
   					progressBarLabel.setText(note);
   				}
   			});
  		} else {
  			progressBarLabel.setText(note);
  		}     
	}
    
	    
    /**
     * Launches settings dialog
     */
	private void settingsAction() {
    	SettingsDialog dialog = new SettingsDialog(this, storage);
		long translationBase = Settings.getLong("translationBasePeriod");
		double translationFactor = Settings.getDouble("translationFactor");
		long reverseBase = Settings.getLong("reverseBasePeriod");
		double reverseFactor = Settings.getDouble("reverseFactor");
		long transcriptionBase = Settings.getLong("transcriptionBasePeriod");
		double transcriptionFactor = Settings.getDouble("transcriptionFactor");
		long gapsFillingBase = Settings.getLong("gapsFillingBasePeriod");
		double gapsFillingFactor = Settings.getDouble("gapsFillingFactor");

    	dialog.setVisible(true);
    	
    	if(dialog.getPressedButton() == SettingsDialog.BTN_OK) {
			long newTranslationBase = Settings.getLong("translationBasePeriod");
			double newTranslationFactor = Settings.getDouble("translationFactor");
			long newReverseBase = Settings.getLong("reverseBasePeriod");
			double newReverseFactor = Settings.getDouble("reverseFactor");
			long newTranscriptionBase = Settings.getLong("transcriptionBasePeriod");
			double newTranscriptionFactor = Settings.getDouble("transcriptionFactor");
			long newGapsFillingBase = Settings.getLong("gapsFillingBasePeriod");
			double newGapsFillingFactor = Settings.getDouble("gapsFillingFactor");
			if(translationBase != newTranslationBase ||
					translationFactor != newTranslationFactor ||
					reverseBase != newReverseBase ||
					reverseFactor != newReverseFactor ||
					transcriptionBase != newTranscriptionBase ||
					transcriptionFactor != newTranscriptionFactor ||
					gapsFillingBase  != newGapsFillingBase ||
					gapsFillingFactor != newGapsFillingFactor) {
				testNeedsWatcher.recheck();
			}
    	}
	}

    
    /**
     * Implements ProgressBar Interface showPBDeterminate function
     * @param max maximum value of progress bar
     * @param note string note that is shown somewhere near progress bar
     */
	public void showPBDeterminate(int max, String note) {
		progressBar.setIndeterminate(false);
		progressBar.setMaximum(max);
		progressBar.setVisible(true);
		progressBarLabel.setText(note);
		progressBarLabel.setVisible(true);
	}

	
	/**
	 * Implements ProgressBar Interface showPBIndeterminate function
	 */
    public void showPBIndeterminate(String note) {
		progressBar.setIndeterminate(true);
		progressBar.setVisible(true);
		progressBarLabel.setText(note);
		progressBarLabel.setVisible(true);
	}

	
	/**
     * Initializes System Tray Icon (Notification Area Icon)
     */
    private void initSystemTray() {
    	//Tray menu initializing
	    trayMenu = new JPopupMenu();
        trayMenuExpandItem = new JMenuItem("<html><b>Expand</b></html>");
        trayMenuQuitItem = new JMenuItem("Quit");        
        
	    trayMenu.add(trayMenuExpandItem);
	    trayMenu.addSeparator();
	    trayMenu.add(trayMenuQuitItem);
        
	    trayMenuExpandItem.addActionListener(
        		new ActionListener() {
        			public void actionPerformed(ActionEvent evt) {
        				setVisible(true);
        				setExtendedState(NORMAL);
        			}
        		});
        
        trayMenuExpandItem.addMouseListener(
        		new MouseAdapter() {
        			public void mouseEntered(MouseEvent e) {
        				trayMenuExpandItem.setForeground(Color.WHITE);
        				trayMenuQuitItem.setForeground(Color.BLACK);
        			}
        			public void mouseExited(MouseEvent e) {
        				trayMenuExpandItem.setForeground(Color.BLACK);
        			}
        		});
        trayMenuExpandItem.setForeground(Color.WHITE);
	    
        trayMenuQuitItem.addActionListener(
        		new ActionListener() {
        			public void actionPerformed(ActionEvent evt) {
		            	exitAction();
		            }
        		});
        trayMenuQuitItem.addMouseListener(
        		new MouseAdapter() {
        			public void mouseEntered(MouseEvent e) {
        				trayMenuQuitItem.setForeground(Color.WHITE);
        				trayMenuExpandItem.setForeground(Color.BLACK);
        			}
        			public void mouseExited(MouseEvent e) {
        				trayMenuQuitItem.setForeground(Color.BLACK);
        			}
        		});
        trayMenu.addPopupMenuListener(
        		new PopupMenuListener() {
        			public void popupMenuCanceled(PopupMenuEvent e) {}
        			
        			public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
        			
        			public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        				trayMenuExpandItem.setForeground(Color.WHITE);
        			}			
        		});
        
        //Tray icon initializing 
	    ImageIcon trayImage = new ImageIcon("icons/16/crammer.png");
	    trayIcon = new TrayIcon(trayImage, "Crammer", trayMenu);
	
	    trayIcon.addActionListener(new ActionListener() {
	    		public void actionPerformed(ActionEvent e) 
	    		{
	    			setVisible(true);
	    			setExtendedState(NORMAL);
	    		} 
	    	});
	
	    trayIcon.addBalloonActionListener(new ActionListener() {
    		public void actionPerformed(ActionEvent e) 
    		{
    			setVisible(true);
    			setExtendedState(NORMAL);
    			if(repeatTestType >= 0) {
    				chooseTestAction(repeatTestType);
    				repeatTestType = -1;
    			}
    		} 
    	});
	    SystemTray tray = SystemTray.getDefaultSystemTray();
	    tray.addTrayIcon(trayIcon);
    }
	
	
	/**
     * Starts currently selected test
     */
    public void testAction() {
    	refreshTestNeeds(testManager.getCurrentTestType());
    	
    	TestTranslationDialog testTranslationDialog;
    	switch(testManager.getCurrentTestType()) {  		
    		case TestManager.TRANSLATION:
    			testTranslationDialog = new TestTranslationDialog(this, 
    					dictionariesManager, TestManager.TRANSLATION, 
    	    				storage);
    			testTranslationDialog.setVisible(true);
    	    	break;
    		case TestManager.REVERSE_TRANSLATION:
    			testTranslationDialog = new TestTranslationDialog(this, 
    					dictionariesManager, TestManager.REVERSE_TRANSLATION, 
	    				storage);
    			testTranslationDialog.setVisible(true);   	    	
	    		break;
    		case TestManager.TRANSCRIPTION:
    			TestTranscriptionDialog transcriptionDialog = 
    				new TestTranscriptionDialog(this, dictionariesManager, 
    						storage);
    			transcriptionDialog.setVisible(true);   	    	
	    		break;
    		case TestManager.GAPS_FILLING:
    			TestGapsFillingDialog gapsFillingDialog = 
    				new TestGapsFillingDialog(this, dictionariesManager, 
    						storage);
    			gapsFillingDialog.setVisible(true);  
	    		break;
    	}    	   	
	}
	
	
	/**
     * Initializes Tool bar
     */
    private void initToolBar() {
        toolBar = new JToolBar(JToolBar.HORIZONTAL);
        toolBar.setFloatable(false);     

        ImageIcon newIcon = new ImageIcon("icons/"+ toolBarIconsSize + "/new.png");
    	newButton = new CrammerToolbarToggleButton(newIcon, "New");

    	popupNew = new JPopupMenu() {
    		public void setVisible(boolean b) {
    			super.setVisible(b);
    			if(!b) {
    				newButton.setSelected(false);
    			}
    		}
    	};
    	ImageIcon newDictIcon = new ImageIcon("icons/16/newDictionary.png");
    	popupNewDictItem = new JMenuItem("Dictionary", newDictIcon);
    	
    	ImageIcon newSheetIcon = new ImageIcon("icons/16/newSheet.png");
    	popupNewSheetItem = new JMenuItem("Sheet", newSheetIcon);
		
    	popupNew.add(popupNewDictItem);
    	popupNew.add(popupNewSheetItem);	
           	
    	popupNewDictItem.addActionListener(new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				popupNew.setVisible(false);
				addDictionary();
			}
		});

    	popupNewSheetItem.addActionListener(new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				popupNew.setVisible(false);				
				addSheet();
			}
		});

		ImageIcon editIcon = new ImageIcon("icons/" + toolBarIconsSize 
				+ "/edit.png");
		editButton = new CrammerToolbarButton(editIcon, "Edit");

		ImageIcon deleteIcon = new ImageIcon("icons/" + toolBarIconsSize 
				+ "/delete.png");
		deleteButton = new CrammerToolbarButton(deleteIcon, "Delete");

        ImageIcon testTranslationIcon = new ImageIcon("icons/" + toolBarIconsSize 
        		+ "/testTranslation.png");
		testTranslationButton = new CrammerToolbarToggleButton(testTranslationIcon, 
				"Set Translation as a current test");

		ImageIcon testReverseIcon = new ImageIcon("icons/" + toolBarIconsSize 
				+ "/testReverseTranslation.png");
		testReverseButton = new CrammerToolbarToggleButton(testReverseIcon,
				"Set Reverse Translation as a current test");
		
		ImageIcon testTranscriptionIcon = new ImageIcon("icons/" 
				+ toolBarIconsSize + "/testTranscription.png");
		testTranscriptionButton = new CrammerToolbarToggleButton(testTranscriptionIcon,
				"Set Transcription as a current test");
		
		ImageIcon testGapsFillingIcon = new ImageIcon("icons/" 
				+ toolBarIconsSize + "/testGapsFilling.png");
		testGapsFillingButton = new CrammerToolbarToggleButton(testGapsFillingIcon, 
				"Set Gaps Filling as a current test");
		
		ImageIcon startTestIcon = new ImageIcon("icons/" + toolBarIconsSize 
				+ "/startTest.png");
		startTestButton = new CrammerToolbarButton(startTestIcon, "Start testing");
		
		ImageIcon settingsIcon = new ImageIcon("icons/" + toolBarIconsSize + 
				"/settings.png");
		settingsButton = new CrammerToolbarButton(settingsIcon, 
				"Customize settings");

		toolBar.add(newButton);
        toolBar.add(editButton);
        toolBar.add(deleteButton);
		toolBar.addSeparator();
        toolBar.add(testTranslationButton);
        toolBar.add(testReverseButton);
        toolBar.add(testTranscriptionButton);
        toolBar.add(testGapsFillingButton);
        toolBar.addSeparator();
        toolBar.add(startTestButton);
        toolBar.addSeparator();
        toolBar.add(settingsButton);

        newButton.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						popupNew.show(newButton, 0, newButton.getHeight());					
					}
				});

        editButton.addActionListener(
        		new AbstractAction() {
        			public void actionPerformed(ActionEvent e) {
        				editAction();
        			}
        		});

        deleteButton.addActionListener(
        		new AbstractAction() {
        			public void actionPerformed(ActionEvent e) {
        				deleteAction();
        			}
        		});

        testTranslationButton.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						chooseTestAction(TestManager.TRANSLATION);
					}
				});

        testReverseButton.addActionListener(
        		new AbstractAction()	{
        			public void actionPerformed(ActionEvent e) {
        				chooseTestAction(TestManager.REVERSE_TRANSLATION);
        			}
        		});

        testTranscriptionButton.addActionListener(
        		new AbstractAction()	{
					public void actionPerformed(ActionEvent e) {
						chooseTestAction(TestManager.TRANSCRIPTION);
					}
				});

        testGapsFillingButton.addActionListener(
        		new AbstractAction() {
        			public void actionPerformed(ActionEvent e) {
        				chooseTestAction(TestManager.GAPS_FILLING);
        			}
        		});

        startTestButton.addActionListener(
        		new AbstractAction() {
        			public void actionPerformed(ActionEvent e) {
        				testAction();
        			}
        		});
        
        settingsButton.addActionListener(
        		new AbstractAction() {
					public void actionPerformed(ActionEvent e) {
						settingsAction();
					}
				});
    }
	
	
	/**
	 * Implements ProgressBar Interface updatePBValue function
	 * @param value new value for progress bar
	 */
	public void updatePBValue(final int value) {
   		if(!SwingUtilities.isEventDispatchThread()) {
   			SwingUtilities.invokeLater(new Runnable() {
   				public void run() {
   					progressBar.setValue(value);
   				}
   			});
  		} else {
  			progressBar.setValue(value);
  		}      		
	}

    
    /**
     * DictionaryTreeTable class extends JTreeTable and used by CrammerMainFrame
     * for representing dictionaries and sheets GUI.
     * @author Konstantin Fadeyev
     *
     */
    private class DictionaryTreeTable extends JTreeTable {
        private final static int WRONG_COLOR_INDEX_MAX = 6;
        private String[] toolTips = {"Dictionary/Sheet Name", 
        		"Words Count (To Test/Total)", 
        		"Choose Dictionary/Sheet To Test"};
        
        private Color[] colors;
        private Color[] selectedColors;
        
        private TestManager testManager;	
    	
    	
    	public DictionaryTreeTable(DictionaryTreeTableModel treeTableModel, 
    			TestManager testManager) {
    		super(treeTableModel);
    		this.testManager = testManager;  
    		
        	colors = new Color[WRONG_COLOR_INDEX_MAX];
        	selectedColors = new Color[WRONG_COLOR_INDEX_MAX];
        	for(int i = 0; i < WRONG_COLOR_INDEX_MAX; i++) {
        		int currentColor = 255 - (255-190)*i/(WRONG_COLOR_INDEX_MAX-1);
        		colors[i] = new Color(255, currentColor, currentColor);
        		selectedColors[i] = new Color(235, currentColor-20, currentColor);
        	}
        	
        	TreeTableCellRenderer renderer = (TreeTableCellRenderer)getDefaultRenderer(TreeTableModel.class);
        	ImageIcon leafIcon = new ImageIcon("icons/16/sheet.png");
        	renderer.setLeafIcon(leafIcon);
        	ImageIcon openIcon = new ImageIcon("icons/16/dictionaryOpened.png");
        	renderer.setOpenIcon(openIcon);
        	ImageIcon closedIcon = new ImageIcon("icons/16/dictionary.png");
        	renderer.setClosedIcon(closedIcon);
    	}
    	
        protected JTableHeader createDefaultTableHeader() {
            return new JTableHeader(columnModel) {
		                public String getToolTipText(MouseEvent e) {
		                    String tip = null;
		                    java.awt.Point p = e.getPoint();
		                    int index = columnModel.getColumnIndexAtX(p.x);
		                    int realIndex = 
		                            columnModel.getColumn(index).getModelIndex();
		                    tip =  toolTips[realIndex];
		                    return tip;
		                }
		            };
        }
        
    	private void defineComponentColors(Component component, 
        		boolean isSelected, int row) {
        	
        	component.setForeground(Color.black);
        	
        	Object node = getComponentForRow(row);
        	if(node == null) {
        		return;
        	}
        	int wrongIndex = getWrongIndex(node, 0);
        	if(!isSelected) {
    			component.setBackground(colors[wrongIndex]);
    		} else {
    			component.setBackground(selectedColors[wrongIndex]);
    		}
        }
    	
    	public void expandDictionary(int dictionaryIndex) {
    		Object dictionaryRoot = treeTableModel.getRoot();
    		tree.expandPath(new TreePath(new Object[] {
    				dictionaryRoot, 
    				treeTableModel.getChild(dictionaryRoot, dictionaryIndex)}));
    	}
    	
    	public void fireTableDataChanged(boolean saveSelection) {
        	int[] rowIndices = getSelectedRows();
        	int[] columnIndices = getSelectedColumns();
        	
        	((DictionaryTreeTableModel)treeTableModel).dataChanged();
        	
        	if(saveSelection) {
        		if(rowIndices.length > 0)
        			for(int i=0; i<rowIndices.length; i++)
        				addRowSelectionInterval(rowIndices[i], 
        						rowIndices[i]);

        		if(columnIndices.length > 0)
        			for(int i=0; i<columnIndices.length; i++)
        				addColumnSelectionInterval(columnIndices[i], 
        						columnIndices[i]);
        	}
        }
    	
    	public Object getComponentForRow(int index) {
    		TreePath path = tree.getPathForRow(index);
    		if(path == null) {
    			return null;
    		}
    		return path.getLastPathComponent();
    	}
    	
    	public Object getSelectedComponent() {
    		int selectedIndex = getSelectedRow();
    		if(selectedIndex < 0) {
    			return null;
    		}
    		TreePath path = tree.getPathForRow(selectedIndex);
    		if(path == null) {
    			return null;
    		}
    		return path.getLastPathComponent();
    	}
    	
    	public int getSelectedDictionary() {
    		int selectedIndex = getSelectedRow();
    		if(selectedIndex < 0) {
    			return -1;
    		}
    		TreePath path = tree.getPathForRow(selectedIndex);
    		if(path == null) {
    			return -1;
    		}
    		if(path.getPathCount() < 2) {
    			return -1;
    		}
    		Object root = treeTableModel.getRoot();
        	Dictionary dictionary = (Dictionary)path.getPathComponent(1);    	
    		return treeTableModel.getIndexOfChild(root, dictionary);
    	}
    	
    	public int getSelectedSheet() {
    		int selectedIndex = getSelectedRow();
    		if(selectedIndex < 0) {
    			return -1;
    		}
    		TreePath path = tree.getPathForRow(selectedIndex);
    		if(path == null) {
    			return -1;
    		}
    		if(path.getPathCount() < 3) {
    			return -1;
    		}
        	Dictionary dictionary = (Dictionary)path.getPathComponent(1);   
        	Sheet sheet = (Sheet)path.getPathComponent(2);
    		return treeTableModel.getIndexOfChild(dictionary, sheet);		
    	}
    	
    	public int getWrongIndex(Object object, int change){
        	int totalWordsNumber = 0;
        	int toTestWordsNumber = 0;
        	if(object instanceof Dictionary) {
        		Dictionary dictionary = (Dictionary)object;    		
        		totalWordsNumber = dictionary.getWordsCount();

        		totalWordsNumber = dictionary.getWordsCount();
        		toTestWordsNumber = dictionary.getTestWordsCount(
        				testManager.getCurrentTestType()) - change;
        	} else if(object instanceof Sheet) {   		
        		Sheet sheet = (Sheet)object;	    	   		   	        	
            	totalWordsNumber = sheet.size();    
            	toTestWordsNumber =  sheet.getTestWordsCount(
            			testManager.getCurrentTestType()) - change;;
        	}

        	if(toTestWordsNumber == 0) {
        		return 0;
        	} else if(toTestWordsNumber == 1 || totalWordsNumber/toTestWordsNumber >= 10) {
        		return 1;
        	} else if(totalWordsNumber/toTestWordsNumber >= 5) {
        		return 2;
        	} else if(totalWordsNumber/toTestWordsNumber >= 3) {
        		return 3;
        	} else if(totalWordsNumber/toTestWordsNumber >= 2) {
        		return 4;	
        	} else {
        		return 5;
        	}    	
        }
    	
        public boolean isExpandedDictionary(int dictionaryIndex) {
        	Object dictionaryRoot = treeTableModel.getRoot();
        	return tree.isExpanded(new TreePath(new Object[] {
        			dictionaryRoot, 
        			treeTableModel.getChild(dictionaryRoot, dictionaryIndex)}));
    	}
        
    	public Component prepareEditor(TableCellEditor editor,
                int row, int column){
    		Component comp = super.prepareEditor(editor, row, column);
    		defineComponentColors(comp, true, row);
    		if(comp instanceof JComponent) {
    			((JComponent)comp).setBorder(null);
    		}
    		return comp;
    	}
    	
    	public Component prepareRenderer(TableCellRenderer renderer,
                int row, int column){
    		Component comp = super.prepareRenderer(renderer, row, column);
    		boolean selected = (row == getSelectedRow());
    		defineComponentColors(comp, selected, row);
    		if(comp instanceof JComponent) {
    			((JComponent)comp).setBorder(null);
    		}
    		if(column==1 && comp instanceof JLabel) {
    			((JLabel)comp).setHorizontalAlignment(JLabel.CENTER);
    		}
    		return comp;
    	}
    	
        public void setSelectedDictionary(int dictionaryIndex) {
    		Object root = treeTableModel.getRoot();
    		TreePath path = new TreePath(new Object[] {
    				root, treeTableModel.getChild(root, dictionaryIndex)});
    		int selectedIndex = tree.getRowForPath(path);
    		setRowSelectionInterval(selectedIndex, selectedIndex);
    		scrollRectToVisible(getCellRect(selectedIndex, 0, true));
    	}
        
        public void setSelectedSheet(int dictionaryIndex, int sheetIndex) {
    		if(!isExpandedDictionary(dictionaryIndex)) {
    			expandDictionary(dictionaryIndex);
    		}
    		Object root = treeTableModel.getRoot();
    		Object dictionaryNode = treeTableModel.getChild(root, dictionaryIndex);
    		TreePath path = new TreePath(new Object[] {
    				root, dictionaryNode, 
    				treeTableModel.getChild(dictionaryNode, sheetIndex)});
    		int selectedIndex = tree.getRowForPath(path);
    		setRowSelectionInterval(selectedIndex, selectedIndex);
    		scrollRectToVisible(getCellRect(selectedIndex, 0, true));
    	}
    }

    
    /**
     * DictionaryTreeTableModel class extends TableModel for 
     * representing dictionaries and sheets in the table in CrammerMainFrame.  
     * @author Konstantin Fadeyev
     *
     */
    private class DictionaryTreeTableModel  extends AbstractTreeTableModel {
        private String[]  cNames = {
        		"Name", 
        		"Words Count", 
        		"To Test"};
        private Class<?>[]  cTypes = { 
        		TreeTableModel.class,
    			String.class, 
    			Boolean.class};
        
        private TestManager testManager;
    	
        
    	public DictionaryTreeTableModel(TestManager testManager, 
    			DictionariesManager dictionariesManager) {
    		super(null);
    		this.testManager = testManager;
    		root = dictionariesManager;
    	}
    	
    	
    	public void dataChanged() {
        	fireTreeNodesChanged(this, 
        			new Object[] {root}, 
        			null, 
        			null);
        }
    	
    	
    	public void dictionaryChanged(int index) {
        	fireTreeNodesChanged(this, 
        			new Object[] {root}, 
        			new int[] {index}, 
        			new Object[] {getChild(root, index)});
        }
       
    	
        public void dictionaryInserted(int index) {
        	fireTreeNodesInserted(this,
        			new Object[] {root},
        			new int[] {index},
        			new Object[] {getChild(root, index)});       
        }
        
        
        public void dictionaryRemoved(int index) { 
        	fireTreeNodesRemoved(this,
        			new Object[] {root},
        			new int[] {index},
        			new Object[] {getChild(root, index)});
        }
        
        
        public Object getChild(Object parent, int index){
    		if(parent instanceof DictionariesManager) {
   				return ((DictionariesManager)parent).getDictionary(index);
    		} else {
   				return ((Dictionary)parent).getSheet(index);
    		}
    	}
        
        
        public int getChildCount(Object parent) {
    		if(parent instanceof DictionariesManager) {
   				return ((DictionariesManager)parent).size();
    		} else {
   				return ((Dictionary)parent).size();
    		}
        }
        
        
        public Class<?> getColumnClass(int column) {
        	return cTypes[column];
        }
        
      
        public int getColumnCount() {
        	return cNames.length;
        }
        
        
        public String getColumnName(int column) {
        	return cNames[column];
        }
        
        
        public Object getValueAt(Object node, int column) {
    		if(node instanceof Dictionary) {
    			Dictionary dictionary = (Dictionary)node;
    			switch(column) {
    			case 0:
    				return dictionary.getName();
    			case 1:
   					return "<html><b>" + dictionary.getToTestWordsCount(
   							testManager.getCurrentTestType())
   							 + "/" + dictionary.getWordsCount() + "</b></html>";
    			default:
    				return dictionary.isToTest();
    			}
    		} else {
    			Sheet sheet = (Sheet)node;
    			switch(column) {
    			case 0:
    				return sheet.getName();
    			case 1:
   					return (new Integer(sheet.getWordsCount())).toString();
    			default:	
    				return sheet.isToTest();
    			}
    		}
        }
        
        
        public boolean isCellEditable(Object node, int column) { 
            return (getColumnClass(column) == TreeTableModel.class ||
            		getColumnClass(column) == Boolean.class); 
        }
        
        
        public boolean isLeaf(Object node) {
    		return (node instanceof Sheet);
        }
        
        
        public void setValueAt(Object aValue, Object node, int column) 
	    {
	    	if(column == 2) {
	    		if(node instanceof Dictionary) {
	    			Dictionary dictionary = (Dictionary)node;
	    			int dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
	    			if((Boolean)aValue) {
	    				refreshDictionaryTestNeeds(dictionaryIndex);
	    				dictionary.setToTest(true);
	    				for(int i=0; i < dictionary.size(); i++) {
	    					Sheet sheet = dictionary.getSheet(i);		    	    		
	    					sheet.setToTest(true);
	    					dictionaryTreeTableModel.sheetChanged(
	    							dictionaryIndex, i);
	    				}
	    			} else {
	    				for(int i=0; i < dictionary.size(); i++) {
	    					Sheet sheet = dictionary.getSheet(i);		    	    		
	    					sheet.setToTest(false);
	    					dictionaryTreeTableModel.sheetChanged(
	    							dictionaryIndex, i);
	    				}
	    				dictionary.setToTest(false);
	    			}
	    		} else if(node instanceof Sheet) {
	    			int dictionaryIndex = dictionaryTreeTable.getSelectedDictionary();
	        		int sheetIndex = dictionaryTreeTable.getSelectedSheet();
	        		Sheet sheet = (Sheet)node;
	        		if((Boolean)aValue) {
		        		refreshSheetTestNeeds(dictionaryIndex, sheetIndex);			    			
		    			Dictionary dictionary = dictionariesManager.getDictionary(dictionaryIndex);
		    			dictionary.setToTest(true);
		    			dictionaryTreeTableModel.dictionaryChanged(
    							dictionaryIndex);
	        		}
	        		sheet.setToTest((Boolean)aValue);
	    		}		
	    		refreshDictionaryWordsCountLabel();
	    		refreshStartTestButton();
	    	}
	    }
        
        
        public void sheetChanged(int dictionaryIndex, int sheetIndex) {  	
        	Dictionary dictionaryNode = (Dictionary)getChild(root, dictionaryIndex);
        	fireTreeNodesChanged(this, 
        			new Object[] {root, dictionaryNode},
        			new int[] {sheetIndex},
        			new Object[] {getChild(dictionaryNode, sheetIndex)});
        	dictionaryChanged(dictionaryIndex);
        }
        
        
        public void sheetInserted(int dictionaryIndex, int sheetIndex) { 
        	Dictionary dictionaryNode = (Dictionary)getChild(root, dictionaryIndex);
        	fireTreeNodesInserted(this, 
        			new Object[] {root, dictionaryNode},
        			new int[] {sheetIndex},
        			new Object[] {getChild(dictionaryNode, sheetIndex)});
        	dictionaryChanged(dictionaryIndex);
        }
        
        
        public void sheetRemoved(int dictionaryIndex, int sheetIndex) {
        	Dictionary dictionaryNode = (Dictionary)getChild(root, dictionaryIndex);   	
        	fireTreeNodesRemoved(this, 
            			new Object[] {root, dictionaryNode},
            			new int[] {sheetIndex},
            			new Object[] {getChild(dictionaryNode, sheetIndex)}); 
        	dictionaryChanged(dictionaryIndex);
        }
    }
}
